#include "sd_thread.h"
#include "sd_log.h"
#include "sd_hashtable.h"

#ifdef _DEBUG_PTHREAD
pthread_mutex_t g_ThreadLocksTable_mutex;
sd_Hashtable* g_ThreadLocksTable = NULL;

sd_Ret sd_RWLock_Init(sd_RWLock* lock)
{
	
	if (g_ThreadLocksTable == NULL)
	{
		pthread_mutex_init(&g_ThreadLocksTable_mutex, NULL);
		pthread_mutex_lock(&g_ThreadLocksTable_mutex);
		sd_HashtableCreate(&g_ThreadLocksTable, 
			sd_HashFunc_uint32, 
			sd_HashKeyCompare_uint32, 
			47,
			20, 
			sizeof(uint32),
			sizeof(uint32),
			NULL);
		pthread_mutex_unlock(&g_ThreadLocksTable_mutex);
	}
	
	pthread_rwlock_init(lock, NULL);
	return SD_RET_OK;
}

sd_Ret sd_RWLock_Destroy(sd_RWLock* lock)
{
	pthread_rwlock_destroy(lock);
	return SD_RET_OK;
}

sd_Ret sd_RWLock_RLock(sd_RWLock* lock)
{
	uint32 tid = sd_getCurrentThreadID();
	uint32 actid;

	if (lock == NULL)
	{
		SD_ASSERT(lock!=NULL);
	}

	pthread_mutex_lock(&g_ThreadLocksTable_mutex);
	if (sd_HashtableLookup(g_ThreadLocksTable, &lock, &actid) != SD_RET_OK)
		actid = 0;
	pthread_mutex_unlock(&g_ThreadLocksTable_mutex);

	pthread_rwlock_rdlock(lock);
	
	/*sd_log("TID = %d acquired rlock %d\n",sd_getCurrentThreadID(), (int)lock);*/
	pthread_mutex_lock(&g_ThreadLocksTable_mutex);
	sd_HashtableInsert(g_ThreadLocksTable, &lock, &tid);
	pthread_mutex_unlock(&g_ThreadLocksTable_mutex);

	return SD_RET_OK;
}

sd_Ret sd_RWLock_WLock(sd_RWLock* lock)
{
	uint32 tid = sd_getCurrentThreadID();
	uint32 actid;

	if (lock == NULL)
	{
		SD_ASSERT(lock!=NULL);
	}

	pthread_mutex_lock(&g_ThreadLocksTable_mutex);
	if (sd_HashtableLookup(g_ThreadLocksTable, &lock, &actid) != SD_RET_OK)
		actid = 0;
	pthread_mutex_unlock(&g_ThreadLocksTable_mutex);

	pthread_rwlock_wrlock(lock);
	/*sd_log("TID = %d acquired wlock %d\n",sd_getCurrentThreadID(), (int)lock);*/
	pthread_mutex_lock(&g_ThreadLocksTable_mutex);
	sd_HashtableInsert(g_ThreadLocksTable, &lock, &tid);
	pthread_mutex_unlock(&g_ThreadLocksTable_mutex);
	return SD_RET_OK;
}

sd_Ret sd_RWLock_Unlock(sd_RWLock* lock)
{
	pthread_rwlock_unlock(lock);

	pthread_mutex_lock(&g_ThreadLocksTable_mutex);
	sd_HashtableDelete(g_ThreadLocksTable, &lock);
	pthread_mutex_unlock(&g_ThreadLocksTable_mutex);

	return SD_RET_OK;
}

sd_Ret sd_RWLock_TryRLock(sd_RWLock* lock)
{
	uint32 tid = sd_getCurrentThreadID();
	sd_Ret ret = pthread_rwlock_tryrdlock(lock) == EBUSY ? SD_RET_LOCK_BUSY:SD_RET_OK;
	if (ret == SD_RET_OK)
	{
		/*sd_log("TID = %d acquired rlock %d\n",sd_getCurrentThreadID(), (int)lock);*/
		pthread_mutex_lock(&g_ThreadLocksTable_mutex);
		sd_HashtableInsert(g_ThreadLocksTable, &lock, &tid);
		pthread_mutex_unlock(&g_ThreadLocksTable_mutex);

	}
	return ret;
}

sd_Ret sd_RWLock_TryWLock(sd_RWLock* lock)
{
	uint32 tid = sd_getCurrentThreadID();
	sd_Ret ret = pthread_rwlock_trywrlock(lock) == EBUSY ? SD_RET_LOCK_BUSY:SD_RET_OK;
	if (ret == SD_RET_OK)
	{
		/*sd_log("TID = %d acquired wlock %d\n",sd_getCurrentThreadID(), (int)lock);*/
		pthread_mutex_lock(&g_ThreadLocksTable_mutex);
		sd_HashtableInsert(g_ThreadLocksTable, &lock, &tid);
		pthread_mutex_unlock(&g_ThreadLocksTable_mutex);
	}
	
	return ret;
}





/*
void sd_SpinLock_Init(sd_SpinLock* lock) 
{
	pthread_spin_init(lock, PTHREAD_PROCESS_PRIVATE);
}
void sd_SpinLock_Destroy(sd_SpinLock* lock) 
{
	pthread_spin_destroy(lock);
}

void sd_SpinLock_Lock(sd_SpinLock* lock) 
{
	pthread_spin_lock(lock);
}

void sd_SpinLock_Unlock(sd_SpinLock* lock) 
{
	pthread_spin_unlock(lock);
}
*/
void sd_SpinLock_Init(sd_SpinLock* lock) 
{
	pthread_mutex_init(lock, NULL);
}
void sd_SpinLock_Destroy(sd_SpinLock* lock) 
{
	pthread_mutex_destroy(lock);
}

void sd_SpinLock_Lock(sd_SpinLock* lock) 
{
	pthread_mutex_lock(lock);
}

void sd_SpinLock_Unlock(sd_SpinLock* lock) 
{
	pthread_mutex_unlock(lock);
}

#endif

sd_Ret sd_RWLock_TimeoutRLock(sd_RWLock* lock, const struct timespec* timeout)
{
	long times = 0;
	while (pthread_rwlock_tryrdlock(lock) == EBUSY)
	{
		sd_sched_yield();
		times++;
		if (times >= timeout->tv_nsec)
		{
			return SD_RET_LOCK_BUSY;
		}
	}
	return SD_RET_OK;
}

sd_Ret sd_RWLock_TimeoutWLock(sd_RWLock* lock, const struct timespec* timeout)
{
	long times = 0;
	while (pthread_rwlock_trywrlock(lock) == EBUSY)
	{
		sd_sched_yield();
		times++;
		if (times >= timeout->tv_nsec)
		{
			return SD_RET_LOCK_BUSY;
		}
	}
	return SD_RET_OK;
}
